"""
安全相关功能：密码加密、JWT Token等
"""
from datetime import datetime, timedelta
from typing import Optional, Dict, Any
from jose import JWTError, jwt
from passlib.context import CryptContext

from app.config import settings


# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """
    验证密码
    
    注意: bcrypt限制密码最长72字节
    """
    try:
        return pwd_context.verify(plain_password, hashed_password)
    except ValueError as e:
        # 如果密码超过72字节，截断后重试
        if "password cannot be longer than 72 bytes" in str(e):
            # 安全地截断到72字节（避免截断多字节UTF-8字符）
            password_bytes = plain_password.encode('utf-8')
            if len(password_bytes) > 72:
                # 从72字节处向前找到完整的UTF-8字符边界
                truncated = password_bytes[:72]
                while len(truncated) > 0:
                    try:
                        plain_password = truncated.decode('utf-8')
                        break
                    except UnicodeDecodeError:
                        truncated = truncated[:-1]
                return pwd_context.verify(plain_password, hashed_password)
        raise


def get_password_hash(password: str) -> str:
    """
    获取密码哈希值
    
    注意: bcrypt限制密码最长72字节
    """
    try:
        return pwd_context.hash(password)
    except ValueError as e:
        # 如果密码超过72字节，截断后重试
        if "password cannot be longer than 72 bytes" in str(e):
            # 安全地截断到72字节（避免截断多字节UTF-8字符）
            password_bytes = password.encode('utf-8')
            if len(password_bytes) > 72:
                # 从72字节处向前找到完整的UTF-8字符边界
                truncated = password_bytes[:72]
                while len(truncated) > 0:
                    try:
                        password = truncated.decode('utf-8')
                        break
                    except UnicodeDecodeError:
                        truncated = truncated[:-1]
                return pwd_context.hash(password)
        raise


def create_access_token(data: Dict[str, Any], expires_delta: Optional[timedelta] = None) -> str:
    """
    创建访问Token
    
    Args:
        data: 要编码的数据（通常包含用户ID）
        expires_delta: 过期时间增量
    
    Returns:
        JWT Token字符串
    """
    to_encode = data.copy()
    
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    
    to_encode.update({
        "exp": expire,
        "iat": datetime.utcnow()
    })
    
    encoded_jwt = jwt.encode(
        to_encode,
        settings.SECRET_KEY,
        algorithm=settings.ALGORITHM
    )
    
    return encoded_jwt


def create_refresh_token(data: Dict[str, Any]) -> str:
    """
    创建刷新Token
    
    Args:
        data: 要编码的数据
    
    Returns:
        JWT Token字符串
    """
    to_encode = data.copy()
    expire = datetime.utcnow() + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
    
    to_encode.update({
        "exp": expire,
        "iat": datetime.utcnow(),
        "type": "refresh"
    })
    
    encoded_jwt = jwt.encode(
        to_encode,
        settings.SECRET_KEY,
        algorithm=settings.ALGORITHM
    )
    
    return encoded_jwt


def verify_token(token: str) -> Optional[Dict[str, Any]]:
    """
    验证Token
    
    Args:
        token: JWT Token字符串
    
    Returns:
        解码后的payload，如果验证失败返回None
    """
    try:
        payload = jwt.decode(
            token,
            settings.SECRET_KEY,
            algorithms=[settings.ALGORITHM]
        )
        return payload
    except JWTError as e:
        import logging
        logging.error(f"JWT验证错误: {e}")
        return None

